package org.bookie.model.services.impl;

import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;

import org.bookie.model.Bet;
import org.bookie.model.BetSlip;
import org.bookie.model.Category;
import org.bookie.model.Event;
import org.bookie.model.Outcome;
import org.bookie.model.Period;
import org.bookie.model.Wager;
import org.bookie.model.services.BetDAO;
import org.hibernate.Criteria;
import org.hibernate.Transaction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;

public class HibernateBetDAO extends GenericHibernateDAO<Bet, Long> implements BetDAO {
	
	private static final String PROPERTY_ID = "id";
	private static final String PROPERTY_SUCCESSFUL_OUTCOME = "successfulOutcome";
	private static final String PROPERTY_END_DATE = "endDate";

	public void finalise(Outcome outcome) {
		Transaction tx = null;
		try {
			tx = getSession().beginTransaction();
			Bet bet = findById(outcome.getBet().getId());
			bet.setSuccessfulOutcome(outcome);
			getSession().saveOrUpdate(bet);
			List bets = getSession().createCriteria(Wager.class)
							.createCriteria("outcome")
									.createCriteria("bet")
										.add(Restrictions.eq(PROPERTY_ID, outcome.getBet().getId()))
											.list();
			for (Iterator iter = bets.iterator(); iter.hasNext();) {
				Wager next = (Wager)iter.next();
				BetSlip bs = next.getBetSlip();
				if (bs.isClosed()) {
					bs.recalcProfit();
					getSession().saveOrUpdate(bs);
					saveTransaction(bs);
				}
			}
		} catch (RuntimeException e) {
			tx.rollback();
			throw e;
		}

	}
	
	@SuppressWarnings("unchecked")
	public List<Bet> findExpired() {
		GregorianCalendar calendar = new GregorianCalendar();
		Criteria criteria = getSession().createCriteria(Bet.class)
				.add(Restrictions.isNull(PROPERTY_SUCCESSFUL_OUTCOME));
		criteria.createCriteria("event")
						.add(Restrictions.lt(PROPERTY_END_DATE, calendar.getTime()));
		return criteria.list();
		
	}
	
	@SuppressWarnings("unchecked")
	public List<Bet> findFinalized(Period period, Category category) {
		Criteria criteria = getSession().createCriteria(Bet.class).add(
				Restrictions.isNotNull(PROPERTY_SUCCESSFUL_OUTCOME));
		Criteria eventCriteria = criteria.createCriteria("event");
		addPeriodCriteria(PROPERTY_END_DATE, eventCriteria, period);
		eventCriteria.addOrder(Order.desc(PROPERTY_END_DATE));
		if (null != category) {
			eventCriteria.createCriteria("category").add(
					Restrictions.eq(PROPERTY_ID, category.getId()));
		}
		return criteria.list();
	}
	
	private void saveTransaction(BetSlip betSlip) {
		// Store a transaction only if the bet slip is closed
		if (betSlip.getProfit().equals(new Double(0))) {
			return;
		}
		betSlip.getUser().getAccount().deposit(betSlip);
	}

	@SuppressWarnings("unchecked")
	public List<Bet> findByEvent(Event event) {
		return getSession().createCriteria(Bet.class).add(
				Restrictions.eq("event.id", event.getId())).list();
	}
	
}
